package cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign;

import cn.maihe.elg.operation.model.bo.GuaranteeTemplatePdfParams;
import cn.maihe.elg.operation.model.bo.ReceiveTemplatePdfParams;
import cn.maihe.elg.operation.model.enums.AttachmentFileType;
import cn.maihe.elg.operation.model.enums.ContractStatus;
import cn.maihe.elg.operation.model.enums.ContractType;
import cn.maihe.elg.operation.model.enums.GuaranteeTemplateType;
import cn.maihe.elg.operation.repository.entity.*;
import cn.maihe.elg.operation.service.business.BidCustomerService;
import cn.maihe.elg.operation.service.business.KtCustomerService;
import cn.maihe.elg.operation.service.business.ProjectInfoService;
import cn.maihe.elg.operation.service.order.AcceptOrderContractService;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.order.GuaranteeAttachmentInfoService;
import cn.maihe.elg.operation.service.system.OrganizationService;
import cn.maihe.elg.operation.supports.electronicseal.kt.KtConfig;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.dto.ContractGetUrlRespDTO;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.dto.ContractPushReqDTO;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.dto.ContractPushRespDTO;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.dto.ContractQueryRespDTO;
import cn.maihe.elg.operation.supports.electronicseal.kt.cloudsign.service.*;
import cn.maihe.elg.operation.supports.system.ElgResourceService;
import cn.maihe.elg.operation.utils.DocFillUtil;
import cn.maihe.elg.operation.utils.StringUtil;
import cn.maihe.elg.operation.utils.ValidateUtil;
import cn.maihe.framework.starter.mybatis.dao.MybatisDaoImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.kinsec.pdfjar.ContractSignInstance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author caixh
 * @Date 2022/7/28.
 */
@Service
@Slf4j
public class KTPdfCloudSignService {

    private final KtConfig ktConfig;
    private final ContractSignInstance csiRegist;
    private final ContractSignInstance csiContract;
    private final ContractSignInstance csiUpload;

    @Resource
    private ElgResourceService elgResourceService;
    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;
    @Resource
    private AcceptOrderContractService acceptOrderContractService;
    @Resource
    private ProjectInfoService projectInfoService;
    @Resource
    private BidCustomerService bidCustomerService;
    @Resource
    private GuaranteeAttachmentInfoService guaranteeAttachmentInfoService;
    @Resource
    private ContractUploadService contractUploadService;
    @Resource
    private ContractGetUrlService contractGetUrlService;
    @Resource
    private ContractQueryService contractQueryService;
    @Resource
    private ContractDownloadPdfService contractDownloadPdfService;
    @Resource
    private OrganizationService organizationService;
    @Resource
    private KtCustomerService ktCustomerService;
    @Resource
    private ContractPushService contractPushService;
    @Resource
    private PdfAutoStampService pdfAutoStampService;


    public KTPdfCloudSignService(KtConfig ktConfig) {
        this.ktConfig = ktConfig;
        csiRegist = new ContractSignInstance();
        csiContract = new ContractSignInstance();
        csiUpload = new ContractSignInstance();
        KtConfig.CloudSign cloudSign = ktConfig.getCloudSign();
        csiRegist.initPost(cloudSign.getRegistServer(), cloudSign.getAppId(), cloudSign.getKey());
        csiContract.initPost(cloudSign.getContractServer(), cloudSign.getAppId(), cloudSign.getKey());
        csiUpload.initUpload(cloudSign.getContractServer(), cloudSign.getAppId(), cloudSign.getSecret());
    }

    /**
     * 凯特云签时返回签署状态及signUrl
     */
    public Map<String, String> buildSignResp(ContractStatus contractStatus, String ktSignUrl) {
        Map<String, String> signResp = Maps.newHashMap();
        signResp.put("status", contractStatus.name());
        if (StringUtil.isNotBlank(ktSignUrl)) {
            signResp.put("url", ktSignUrl);
        }
        return signResp;
    }

    public String reGetSignUrl(AcceptOrderContract acceptOrderContract, String bidderCreditCode) {
        String acceptOrderNo = acceptOrderContract.getAcceptOrderNo();
        String sid = acceptOrderContract.getChannelContractId();
        ContractGetUrlRespDTO urlRespDTO = contractGetUrlService.getContractSignUrl(acceptOrderNo, acceptOrderNo, bidderCreditCode, sid);
        String signUrl = urlRespDTO.getUrl();
        acceptOrderContract.setContractName(signUrl);
        acceptOrderContract.setContractStatus(ContractStatus.SIGNING.name());
        acceptOrderContract.setGmtModified(Date.from(Instant.now()));
        acceptOrderContractService.updateById(acceptOrderContract);
        return signUrl;
    }

    /**
     * 申请书 协议书签章
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> doKtCloudSign4ApplyAndProtocol(Long acceptOrderId, Long manualProjectId) {
        AcceptOrderInfoDO curDO = acceptOrderInfoService.selectById(acceptOrderId);
//        curDO.setManualProjectId(manualProjectId);

        String acceptOrderNo = curDO.getAcceptOrderNo();
        Long bidCustomerId = curDO.getBidCustomerId();
        BidCustomerDO bidCustomer = bidCustomerService.selectById(bidCustomerId);

        //1.是否存在已签署文件协议
        GuaranteeAttachmentInfoDO applyAttachFile = guaranteeAttachmentInfoService.getTypeAttachmentByAcceptOrderNo(acceptOrderNo, AttachmentFileType.TYPE_11);
//        GuaranteeAttachmentInfoDO protocolAttachFile = guaranteeAttachmentInfoService.getTypeAttachmentByAcceptOrderNo(acceptOrderNo, AttachmentFileType.TYPE_17);
//        if (applyAttachFile == null || protocolAttachFile == null) {
        if (applyAttachFile == null) {

            AcceptOrderContract acceptOrderContract = acceptOrderContractService.getAcceptOrderContract(acceptOrderNo, ContractType.SQ);
            if (acceptOrderContract == null) {
                //上传签署文件、推送签署数据以及获取签署url
                String signUrl = contractUploadService.contractUpload(curDO);
                return this.buildSignResp(ContractStatus.SIGNING, signUrl);
            } else {
                ContractStatus status = ContractStatus.resolve(acceptOrderContract.getContractStatus());
                if (status == ContractStatus.CREATED || status == ContractStatus.OVERDUE)//url失效
                {
                    //重新获取签署url
                    String signUrl = this.reGetSignUrl(acceptOrderContract, bidCustomer.getBidderCreditCode());
                    return this.buildSignResp(ContractStatus.SIGNING, signUrl);
                } else if (status == ContractStatus.SIGNING) {
                    //查询签署状态
                    ContractStatus queryStatus = this.contractQueryAndDownload(curDO, acceptOrderContract);
                    if (queryStatus == ContractStatus.FINISHED) {
                        Map result = this.buildSignResp(ContractStatus.FINISHED, null);
                        return result;
                    } else if (queryStatus == ContractStatus.OVERDUE) {
                        //重新获取签署url
                        String signUrl = this.reGetSignUrl(acceptOrderContract, bidCustomer.getBidderCreditCode());
                        return this.buildSignResp(ContractStatus.SIGNING, signUrl);
                    } else if (queryStatus == ContractStatus.SIGNING) {
                        //重新获取签署url
                        String signUrl = this.reGetSignUrl(acceptOrderContract, bidCustomer.getBidderCreditCode());
                        return this.buildSignResp(ContractStatus.SIGNING, signUrl);
                    } else {
                        throw new RuntimeException("查询合同状态[" + queryStatus + "]暂无法处理");
                    }
                } else {
                    // 其他合同状态 默认也是重新获取url
                    String signUrl = this.reGetSignUrl(acceptOrderContract, bidCustomer.getBidderCreditCode());
                    return this.buildSignResp(ContractStatus.SIGNING, signUrl);
                }
            }
        } else {
            //已存在申请书及协议书的签章文件
            return this.buildSignResp(ContractStatus.FINISHED, null);
        }
    }

    /**
     * 到账证明签章
     * 返回 电签后的pdf文件
     */
    public Path receivePdfSeal(String acceptOrderNo, ReceiveTemplatePdfParams params) {
        Assert.hasText(acceptOrderNo, "受理订单号不能为空");
        ValidateUtil.validate(params);

        //模板填充docx
        Path targetDocPath = elgResourceService.buildReceiveFileByTemplate(acceptOrderNo, params);
        //docx转pdf
        Path targetPdfPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, "receivePdfUnSeal", "pdf");
        //docx转pdf
        targetPdfPath = DocFillUtil.doc2pdf(targetDocPath, targetPdfPath, null);

        //签章后的pdf文件
        Path targetSealPdfPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, "receivePdfSeal", "pdf");

        AcceptOrderInfoDO curDO = acceptOrderInfoService.getByAcceptOrderNo(acceptOrderNo);
        Long orgId = curDO.getOrgId();
        String traceId = acceptOrderNo;

        Map<String, String> fileNameAndPathMap = Maps.newHashMap();
        String receivePdfFileKey = "receivePdfFile";
        String receivePdfFileName = "PDF到账证明";
        fileNameAndPathMap.put(receivePdfFileKey, targetPdfPath.normalize().toString());
        //1.上传签署文件
        Map respDTO = contractUploadService.doService(traceId, fileNameAndPathMap);
        String receivePdfFileId = (String) respDTO.get(receivePdfFileKey);

        OrganizationDO organizationDO = organizationService.getOrganization(orgId);
        KtCustomerDO orgKtCustomerDO = ktCustomerService.selectByOrgId(orgId, "1");

        String orgFullName = organizationDO.getFullName();
        String orgCreditCode = organizationDO.getCreditCode();
        String orgAuthMobile = orgKtCustomerDO.getAutherMobile();

        ContractPushReqDTO.SignFileInfo receiveSignFileInfo = ContractPushReqDTO.buildSignFileInfo(receivePdfFileId, receivePdfFileName)
                .addSignInfo(orgCreditCode, orgFullName, "保函开立人：" + orgFullName, orgAuthMobile,"1");
        List<ContractPushReqDTO.SignFileInfo> signFileInfoList = Lists.newArrayList(receiveSignFileInfo);

        ContractPushReqDTO reqDTO = ContractPushReqDTO.buildReqDTO("到账证明签署", signFileInfoList);
        //2.推送签署数据
        ContractPushRespDTO pushResp = contractPushService.doService(traceId, reqDTO);
        String sid = pushResp.getSid();

        //3.担保机构公章自动签章
        boolean ztPdfSignTF = pdfAutoStampService.pdfAutoStamp(acceptOrderNo, sid,"1", orgCreditCode, orgKtCustomerDO.getToken());
        if (!ztPdfSignTF) {
            throw new RuntimeException("担保机构公章签章失败");
        }

        //4.查询签署状态
        ContractQueryRespDTO queryRespDTO = contractQueryService.queryContractStatus(acceptOrderNo, sid);
        String signState = queryRespDTO.getSignState();
        ContractStatus contractStatus = null;
        if (signState.equals("3")) {
            List<ContractQueryRespDTO.FileArr> fileArr = queryRespDTO.getFileArr();
            fileArr.forEach(arr -> {
                byte[] fileBytes = contractDownloadPdfService.downloadPdf(acceptOrderNo, arr.getFileId());
                try {
                    Files.write(targetSealPdfPath, fileBytes);
                } catch (Exception e) {
                    throw new RuntimeException("下载保存到账证明签署文件异常:" + e.getMessage());
                }
                String remark = String.format("%s:%s", arr.getFileId(), arr.getFileName());
                guaranteeAttachmentInfoService.saveOrUpdateAttachmentInfo(curDO, targetSealPdfPath, AttachmentFileType.TYPE_6, remark);
            });
            contractStatus = ContractStatus.FINISHED;
        } else if (signState.equals("4") || signState.equals("5") || signState.equals("7")) {
            contractStatus = ContractStatus.OVERDUE;
        } else if (signState.equals("2")) {
            contractStatus = ContractStatus.SIGNING;
        } else {
            throw new RuntimeException("签署状态[" + signState + "]暂无法识别");
        }

        if (contractStatus == ContractStatus.FINISHED) {
            try {
                Files.deleteIfExists(targetDocPath);
                Files.deleteIfExists(targetPdfPath);
            } catch (Exception e) {
                log.error("删除投保单号[{}]源待签章到账证明文件异常", acceptOrderNo, e.getMessage(), e);
            }
            return targetSealPdfPath;
        } else {
            throw new RuntimeException("到账证明凯特pdf云签章失败");
        }
    }


    /**
     * 电子保函文件签章
     * 返回 电签后的pdf文件
     */
    public Path guaranteePdfSeal(String acceptOrderNo, GuaranteeTemplatePdfParams params) {
        Assert.hasText(acceptOrderNo, "受理订单号不能为空");
        ValidateUtil.validate(params);

        //模板填充docx
        Path targetDocPath = elgResourceService.buildGuaranteeFileByTemplate(acceptOrderNo, params);
        //docx转pdf
        Path targetPdfPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, "guaranteePdfUnSeal", "pdf");
        //docx转pdf
        targetPdfPath = DocFillUtil.doc2pdf(targetDocPath, targetPdfPath, null);

        //签章后的pdf文件
        Path targetSealPdfPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, "guaranteePdfSeal", "pdf");
        AcceptOrderInfoDO curDO = acceptOrderInfoService.getByAcceptOrderNo(acceptOrderNo);
        Long orgId = curDO.getOrgId();
        String traceId = acceptOrderNo;

        Map<String, String> fileNameAndPathMap = Maps.newHashMap();
        String guaranteePdfFileKey = "guaranteePdfFile";
        String guaranteePdfFileName = "PDF电子保函";
        fileNameAndPathMap.put(guaranteePdfFileKey, targetPdfPath.normalize().toString());
        //1.上传签署文件
        Map respDTO = contractUploadService.doService(traceId, fileNameAndPathMap);
        String guaranteePdfFileId = (String) respDTO.get(guaranteePdfFileKey);

        OrganizationDO organizationDO = organizationService.getOrganization(orgId);
        KtCustomerDO orgKtCustomerDO = ktCustomerService.selectByOrgId(orgId, "1");
        KtCustomerDO legalKtCustomerDO = ktCustomerService.selectByOrgId(orgId, "2");

        String orgFullName = organizationDO.getFullName();
        String orgCreditCode = organizationDO.getCreditCode();
        String orgAuthMobile = orgKtCustomerDO.getAutherMobile();

        String legalName = organizationDO.getLegalName();
        String legalMobile = organizationDO.getLegalMobile();
        String legalAuthMobile = legalKtCustomerDO.getAutherMobile();

        GuaranteeTemplateType type = GuaranteeTemplateType.resolve(curDO.getTemplateId());

        ContractPushReqDTO.SignFileInfo guaranteePdfSignFileInfo = ContractPushReqDTO.buildSignFileInfo(guaranteePdfFileId, guaranteePdfFileName)
                .addSignInfo(orgCreditCode, orgFullName, type.getOrgKey(), orgAuthMobile,"1")
                .addSignInfo(legalMobile, legalName, type.getLeagalKey(),-30,0, legalAuthMobile,"2");

        List<ContractPushReqDTO.SignFileInfo> signFileInfoList = Lists.newArrayList(guaranteePdfSignFileInfo);

        ContractPushReqDTO reqDTO = ContractPushReqDTO.buildReqDTO("PDF电子保函签署", signFileInfoList);
        //2.推送签署数据
        ContractPushRespDTO pushResp = contractPushService.doService(traceId, reqDTO);
        String sid = pushResp.getSid();

        //3.担保机构公章法人章自动签章
        boolean ztPdfSignTF = pdfAutoStampService.pdfAutoStamp(acceptOrderNo, sid,"1", orgCreditCode, orgKtCustomerDO.getToken());
        if (!ztPdfSignTF) {
            throw new RuntimeException("担保机构公章签章失败");
        }
        ztPdfSignTF = pdfAutoStampService.pdfAutoStamp(acceptOrderNo, sid,"2", legalMobile, legalKtCustomerDO.getToken());
        if (!ztPdfSignTF) {
            throw new RuntimeException("担保机构法人章签章失败");
        }

        //4.查询签署状态
        ContractQueryRespDTO queryRespDTO = contractQueryService.queryContractStatus(acceptOrderNo, sid);
        String signState = queryRespDTO.getSignState();
        ContractStatus contractStatus = null;
        if (signState.equals("3")) {
            List<ContractQueryRespDTO.FileArr> fileArr = queryRespDTO.getFileArr();
            fileArr.forEach(arr -> {
                byte[] fileBytes = contractDownloadPdfService.downloadPdf(acceptOrderNo, arr.getFileId());
                try {
                    Files.write(targetSealPdfPath, fileBytes);
                } catch (Exception e) {
                    throw new RuntimeException("下载保存PDF电子保函签署文件异常:" + e.getMessage());
                }
                String remark = String.format("%s:%s", arr.getFileId(), arr.getFileName());
                guaranteeAttachmentInfoService.saveOrUpdateAttachmentInfo(curDO, targetSealPdfPath, AttachmentFileType.TYPE_6, remark);
            });
            contractStatus = ContractStatus.FINISHED;
        } else if (signState.equals("4") || signState.equals("5") || signState.equals("7")) {
            contractStatus = ContractStatus.OVERDUE;
        } else if (signState.equals("2")) {
            contractStatus = ContractStatus.SIGNING;
        } else {
            throw new RuntimeException("签署状态[" + signState + "]暂无法识别");
        }

        if (contractStatus == ContractStatus.FINISHED) {
            try {
                Files.deleteIfExists(targetDocPath);
                Files.deleteIfExists(targetPdfPath);
            } catch (Exception e) {
                log.error("删除投保单号[{}]源待签章保函文件异常", acceptOrderNo, e.getMessage(), e);
            }
            return targetSealPdfPath;
        } else {
            throw new RuntimeException("电子保函凯特pdf云签失败");
        }
    }



    /**
     * 查询合同签署状态及更新本地合同状态并返回合同状态
     */
    @Transactional(rollbackFor = Exception.class)
    public ContractStatus contractQueryAndDownload(AcceptOrderInfoDO orderInfo, AcceptOrderContract acceptOrderContract) {
        //查询签署状态
        String acceptOrderNo = acceptOrderContract.getAcceptOrderNo();
        ContractQueryRespDTO queryRespDTO = contractQueryService.queryContractStatus(acceptOrderNo, acceptOrderContract.getChannelContractId());
        String signState = queryRespDTO.getSignState();
        ContractStatus contractStatus = null;
        if (signState.equals("3")) {
            List<ContractQueryRespDTO.FileArr> fileArr = queryRespDTO.getFileArr();
            fileArr.forEach(arr -> {
                AttachmentFileType attachmentFileType = null;
                if (ContractUploadService.applyFileName.equals(arr.getFileName())) {
                    attachmentFileType = AttachmentFileType.TYPE_11;
                } else if (ContractUploadService.protocolFileName.equals(arr.getFileName())) {
//                    attachmentFileType = AttachmentFileType.TYPE_17;
                }
                byte[] fileBytes = contractDownloadPdfService.downloadPdf(acceptOrderNo, arr.getFileId());
                if (attachmentFileType != null) {
                    Path filePath = elgResourceService.buildOfdPath(acceptOrderNo, attachmentFileType.getDesc(), "pdf");
                    try {
                        Files.write(filePath, fileBytes);
                    } catch (Exception e) {
                        throw new RuntimeException("下载保存[" + attachmentFileType.getDesc() + "]异常:" + e.getMessage());
                    }
                    String remark = String.format("%s:%s", arr.getFileId(), arr.getFileName());
                    guaranteeAttachmentInfoService.saveOrUpdateAttachmentInfo(orderInfo, filePath, attachmentFileType, remark);
                }
            });
            contractStatus = ContractStatus.FINISHED;
        } else if (signState.equals("4") || signState.equals("5") || signState.equals("7")) {
            contractStatus = ContractStatus.OVERDUE;
        } else if (signState.equals("2")) {
            contractStatus = ContractStatus.SIGNING;
        } else {
            throw new RuntimeException("签署状态[" + signState + "]暂无法识别");
        }
        acceptOrderContract.setContractStatus(contractStatus.name());
        MybatisDaoImpl.run().updateById(acceptOrderContract);
        return contractStatus;
    }


}
